#ifndef TREESTRUCT_H
#define TREESTRUCT_H

#include <stdio.h>
#include <stdlib.h>

/* --------------- */
/* Type definition */
/* --------------- */

enum treetype {operateur_node, number_node, boolean_node, variable_node};
enum ast_ops {Empty_OP, PROGRAM_OP, DATADEF_OP, BODY_OP, CONSTLIST_OP, CONSTDEF_OP, TYPEDEFLIST_OP, TYPEDEF_OP, ARRAYDEF_OP, RANGE_OP, RECORDDEF_OP, RECORDLIST_OP, RECORDIDLIST_OP, IDENTIFIERLIST_OP, VARLIST_OP, VARTYPE_OP, LT_OP, EQUAL_OP, GT_OP, LE_OP, NOTEQUAL_OP, GE_OP, PLUS_OP, MINUS_OP, OR_OP, STAR_OP, DIV_OP, MOD_OP, AND_OP, NOT_OP, ARRAYACCES_OP, RECORDACCES_OP, FUNCCALL_OP, ASSIGNMENT_OP, PROCCALL_OP, ARGLIST_OP, ARG_OP, IFTHEN_OP, IFTHENELSE_OP, WHILE_OP, COMPOUND_OP, STATEMENTLIST_OP, STATEMENT_OP, PROCFUNCLIST_OP, FUNCDEF_OP, FUNCBLOCK_OP, PROCDEF_OP, PROCBLOCK_OP, PARADEFLIST_OP, PARATYPE_VAR_OP, PARATYPE_NOVAR_OP, PARALIST_OP};


/* ------------------------------------ */
/* Simple tree, no static analysis done */
/* ------------------------------------ */

typedef struct mytree
{
	enum treetype nodetype;
    struct operateur_struct
    {
        struct mytree *first, *second, *third;
        enum ast_ops operateur;
    } an_operateur;
    int a_number;
    int a_boolean;
    char* a_variable;
} tree;

static const char* ast_ops_printer(enum ast_ops op)
{
	switch (op)
	{
	    case Empty_OP: return "Empty";
        case PROGRAM_OP: return "PROGRAM";
        case DATADEF_OP: return "DATADEF";
        case BODY_OP: return "BODY";
        case CONSTLIST_OP: return "CONSTLIST";
        case CONSTDEF_OP: return "CONSTDEF";
        case TYPEDEFLIST_OP: return "TYPEDEFLIST";
        case TYPEDEF_OP: return "TYPEDEF";
        case ARRAYDEF_OP: return "ARRAYDEF";
        case RANGE_OP: return "RANGE";
        case RECORDDEF_OP: return "RECORDDEF";
        case RECORDLIST_OP: return "RECORDLIST";
        case RECORDIDLIST_OP: return "RECORDIDLIST";
        case IDENTIFIERLIST_OP: return "IDENTIFIERLIST";
        case VARLIST_OP: return "VARLIST";
        case VARTYPE_OP: return "VARTYPE";
        case LT_OP: return "LT";
        case EQUAL_OP: return "EQUAL";
        case GT_OP: return "GT";
        case LE_OP: return "LE";
        case NOTEQUAL_OP: return "NOTEQUAL";
        case GE_OP: return "GE";
        case PLUS_OP: return "PLUS";
        case MINUS_OP: return "MINUS";
        case OR_OP: return "OR";
        case STAR_OP: return "STAR";
        case DIV_OP: return "DIV";
        case MOD_OP: return "MOD";
        case AND_OP: return "AND";
        case NOT_OP: return "NOT_OP";
        case ARRAYACCES_OP: return "ARRAYACCES";
        case RECORDACCES_OP: return "RECORDACCES";
        case FUNCCALL_OP: return "FUNCCALL";
        case ASSIGNMENT_OP: return "ASSIGNMENT";
        case PROCCALL_OP: return "PROCCALL";
        case ARGLIST_OP: return "ARGLIST";
        case ARG_OP: return "ARG";
        case IFTHEN_OP: return "IFTHEN";
        case IFTHENELSE_OP: return "IFTHENELSE";
        case WHILE_OP: return "WHILE";
        case COMPOUND_OP: return "COMPOUND";
        case STATEMENTLIST_OP: return "STATEMENTLIST";
        case STATEMENT_OP: return "STATEMENT";
        case PROCFUNCLIST_OP: return "PROCFUNCLIST";
        case FUNCDEF_OP: return "FUNCDEF";
        case FUNCBLOCK_OP: return "FUNCBLOCK";
        case PROCDEF_OP: return "PROCDEF";
        case PROCBLOCK_OP: return "PROCBLOCK";
        case PARADEFLIST_OP: return "PARADEFLIST";
        case PARATYPE_VAR_OP: return "PARATYPE_VAR";
        case PARATYPE_NOVAR_OP: return "PARATYPE_NOVAR";
        case PARALIST_OP: return "PARALIST";
        default: return "ERROR in AST_OP's printer";
	}
}

static tree *make_operateur (tree *fi, tree *sec, tree *th, enum ast_ops o)
{
	tree *result= (tree*) malloc (sizeof(tree));
	result->nodetype = operateur_node;
	result->an_operateur.first = fi;
	result->an_operateur.second = sec;
	result->an_operateur.third = th;
	result->an_operateur.operateur = o;
	return result;
}

static tree *make_number (int n)
{
	tree *result = (tree*) malloc (sizeof(tree));
	result->nodetype = number_node;
	result->a_number = n;
	return result;
}

static tree *make_boolean (int b)
{
	tree *result = (tree*) malloc (sizeof(tree));
	result->nodetype = boolean_node;
	result->a_boolean = b;
	return result;
}


static tree *make_variable (char* v)
{
	tree *result = (tree*) malloc (sizeof(tree));
	result->nodetype = variable_node;
	result->a_variable = v;
	return result;
}

static void indent(int num)
{
	int i;
	for(i=0;i<num;i++)
	{
		printf(" ");
	}
}

/*void findent(int num)
{
	int i;
	for(i=0;i<num;i++)
	{
		fprintf(yyout, " ");
	}
}//*/

static void printtree (tree *t, int level)
{
	if (t)
	switch (t->nodetype)
	{
		case operateur_node:
			indent(level);
			printf("%s ", ast_ops_printer(t->an_operateur.operateur));
			if (t->an_operateur.second)
			{
				printf("first node\n");
				printtree(t->an_operateur.first, (level+2));
				indent(level);
				printf("%s second node\n", ast_ops_printer(t->an_operateur.operateur));
				printtree(t->an_operateur.second, (level+2));
				if (t->an_operateur.third)
				{
					indent(level);
					printf("%s third node\n", ast_ops_printer(t->an_operateur.operateur));
					printtree(t->an_operateur.third, (level+2));
				}
			}
			else
			{
				printf("node\n");
				printtree(t->an_operateur.first, (level+2));
			}
			break;
		case number_node:
			indent(level);
			printf("%d\n", t->a_number);
			break;
		case boolean_node:
			indent(level);
			if (t->a_boolean)
			{
				printf("True\n");
			}
			else
			{
				printf("False\n");
			}
			break;
		case variable_node:
			indent(level);
			printf("%s\n", t->a_variable);
	}
}

/*static void fprinttree (tree *t, int level)
{
	if (t)
	switch (t->nodetype)
	{
		case operateur_node:
			findent(level);
			fprintf(yyout, "%s ", ast_ops_printer(t->an_operateur.operateur));
			if (t->an_operateur.second)
			{
				fprintf(yyout, "first node\n");
				fprinttree(t->an_operateur.first, (level+2));
				findent(level);
				fprintf(yyout, "%s second node\n", ast_ops_printer(t->an_operateur.operateur));
				fprinttree(t->an_operateur.second, (level+2));
				if (t->an_operateur.third)
				{
					findent(level);
					fprintf(yyout, "%s third node\n", ast_ops_printer(t->an_operateur.operateur));
					fprinttree(t->an_operateur.third, (level+2));
				}
			}
			else
			{
				fprintf(yyout, "node\n");
				fprinttree(t->an_operateur.first, (level+2));
			}
			break;
		case number_node:
			findent(level);
			fprintf(yyout, "%d\n", t->a_number);
			break;
		case boolean_node:
			findent(level);
			if (t->a_boolean)
			{
				fprintf(yyout, "True\n");
			}
			else
			{
				fprintf(yyout, "False\n");
			}
			break;
		case variable_node:
			findent(level);
			fprintf(yyout, "%s\n", t->a_variable);
	}
}///*/

tree* lexer_parser(char* input_file);
#endif
